home *** CD-ROM | disk | FTP | other *** search
- /* March 94 Challenge - BitMapToText
- ** by Bob Boonstra
- **
- ** Strategy
- **
- ** The problem states that "the smallest detail in the
- ** input image [will] be roughly equal to or larger than
- ** a single character of the given font and font size.
- ** Therefore, this solution attempts only to match the
- ** number of bits set in a given character size piece
- ** of the image with the number of bits set in the
- ** character chosen to represent that piece of the
- ** image.
- ** The strategy is to:
- ** (1) draw the characters from 32 to 127 in an
- ** offscreen bitmap.
- ** (2) sort the characters in order of increasing
- ** number of bits set
- ** (3) precalculate a mapping from pixel density to
- ** output characters
- ** (4) loop thru the character size chunks of the image,
- ** count the number of bits set, and output the
- ** corresponding character.
- **
- ** Assumptions
- ** Width of characters is assumed to be <=32 pixels
- ** (reasonable for (6-24 point mono font)
- ** No assumption that actual height of font <= 24;
- ** ascent+descent+leading may exceed point size
- ** Ref NIM: Text pg 4-11
- ** bitMapPtr->rowBytes * (font height) assumed < 32K
- */
-
- #include <stdio.h>
-
- #pragma options (honor_register, !assign_registers)
-
- #define uchar unsigned char
- #define ushort unsigned short
- #define ulong unsigned long
-
- #define EOL 0x0d
- #define kErr 1
- #define kFirstChar 32
- #define kLastCharPlus1 128
- #define kNumChars (kLastCharPlus1-kFirstChar)
- #define kBytesPerBMChar sizeof(long)
- #define kMaxCharWidth (8*kBytesPerBMChar)
- #define kCharRowBytes 384
- #define kBitsPerChunk 32
- #define kMaxCharVals 512
-
- #define DoSetMem(addr,sz,val) \
- { register long *p = (long *)addr; \
- register short count = sz; \
- do *p++=val; while (--count); \
- }
-
- // Macro BitCount(x,count) increments count for each bit
- // in x set to 1.
- // WARNING: the expression (x=x--&x) in the BitCount
- // macro is not portable, because the order of evaluation
- // is undefined, but it generates correct fast code
- // for (x=x&(x-1)) in THINK C.
- // Non-portable code is BAD FOR YOU, except where speed
- // is very important, like in this Challenge.
- #define BitCount(x,initVal,count) \
- if (x=initVal) do ++count; while (x = x--&x);
-
- ushort lineHeight,charWid;
- FontInfo fInfo;
-
- short InitOffscreenBitMap(GrafPort *charPtr)
- {
- //
- // Initialize font information
- //
- Point scalePt = {1,1};
- ulong numBytes;
- GetFontInfo(&fInfo);
- lineHeight = fInfo.leading+fInfo.ascent+fInfo.descent;
- charPtr->pnLoc.v = lineHeight -fInfo.descent;
- //
- // Initialize GrafPort and bitmap storage
- //
- numBytes = lineHeight*kCharRowBytes;
- if (0 == (charPtr->portBits.baseAddr =
- (QDPtr)NewPtr( numBytes )))
- return kErr;
- DoSetMem(charPtr->portBits.baseAddr,
- numBytes/sizeof(long),0);
- charPtr->portBits.rowBytes = kCharRowBytes;
- charPtr->portBits.bounds.top = 0;
- charPtr->portBits.bounds.left = 0;
- charPtr->portBits.bounds.bottom = lineHeight;
- charPtr->portBits.bounds.right = kCharRowBytes*8;
- RectRgn(charPtr->visRgn,&charPtr->portBits.bounds);
- charWid = StdTxMeas(1,"W",&scalePt,&scalePt,&fInfo);
- /*if (charWid != fInfo.widMax) DebugStr("\p bad wid");*/
- if (kBytesPerBMChar*8 < charWid) return kErr;
- return 0;
- }
-
- //
- // Draw the characters of the given font into an offscreen
- // bitmap.
- //
- void DrawTheChars(GrafPtr charPort)
- {
- register Point scalePt = {1,1};
- register short hPos = kMaxCharWidth-charWid;
- register short count;
- uchar chVal = kFirstChar;
- count = kNumChars; do {
- charPort->pnLoc.h = hPos;
- StdText(1,&chVal,scalePt,scalePt);
- hPos += kMaxCharWidth;
- ++chVal;
- } while (--count);
- }
-
- //
- // Calculate the number of bits set in each character, for
- // subsequent use in comparing to a section of the bitmap.
- //
- void InitBitsSetArray(register char *p,register ushort *c)
- {
- register short count;
- p += (ushort)fInfo.leading*kCharRowBytes;
- count = kNumChars; do {
- register ushort bitcount=0;
- register uchar *q = (uchar *)p;
- register short vCount;
- vCount = lineHeight-fInfo.leading; do {
- register ulong row;
- BitCount(row,*(ulong *)q,bitcount);
- q += kCharRowBytes;
- } while (--vCount);
- // Following line fudges the density value for characters
- // to account for the fact that the most dense character
- // is significantly less dense than a dark section of a
- // bitmap.
- bitcount += bitcount>>1;
- *c++ = bitcount;
- p += kBytesPerBMChar;
- } while (--count);
- }
-
- //
- // Sort the characters in order of increasing number of
- // bits set (density).
- //
- void SortBitsSetArray(register ushort *v,
- register ushort *c)
- {
- register ushort *x;
- register ushort count,val,xVal,newVal;
- // Initialize sort order
- count = kNumChars; val = 0; x=c; do {
- *x++ = val; ++val;
- } while (--count);
- // Bidirectional exchange sort is good enough for this small
- // array
- count = kNumChars-1;
- x = c;
- val = *(v+*c);
- do {
- ushort *saveC;
- ushort saveCount;
- xVal = *(c+1);
- newVal = *(v+xVal);
- if (val > /**x*/ newVal) {
- // Swap pointers
- *(c+1) = *c; *c = xVal;
- if (count < kNumChars-1) {
- saveC=c+1; saveCount=count; val = *(v+*c);
- do {
- xVal = *(c-1); x = v+xVal; newVal = *x;
- if (val >= newVal) break;
- *(c-1) = *c; *c = xVal; --c;
- } while (++count < kNumChars-1);
- count = saveCount; c=saveC; val = *(v+*c);
- } else {
- val = *(v+*c++);
- }
- } else {
- val = newVal; ++c;
- }
- } while (--count);
- }
-
- //
- // Initialize a mapping from number of bits set in a
- // character-sized section of the bitmap to the character
- // used to represent that section.
- //
- void InitCharPointerArray(register ushort *v,
- register ushort *c, register ushort *p)
- {
- register short count1,count2,count3;
- register short currentVal;
- count2 = kNumChars;
- count1 = -1;
- do {
- currentVal = *(v+*c);
- if (currentVal>count1) {
- count3 = (ushort)(currentVal-count1)/2;
- if (count3) do {
- *p++=*(p-1);
- if (++count1 >= kMaxCharVals) return;
- } while (--count3);
- do {
- *p++=' '+*c;
- if (++count1 >= kMaxCharVals) return;
- } while (currentVal>count1);
- }
- ++c;
- } while (--count2);
- do {
- *p++=*(p-1);
- } while (++count1<kMaxCharVals);
- }
-
- short BitMapToText(bitMapPtr,fontName,fontSize,outputFile)
- BitMap *bitMapPtr;
- Str255 fontName;
- unsigned short fontSize;
- FILE *outputFile;
- {
- GrafPort charPort;
- GrafPtr savePort;
-
- //
- // bitsSet[x] is the number of bits set to 1 in the
- // representation of character ' '+x
- // sortedCharP[y]+' ' is the y-th character in order of
- // increasing number of bits set
- //
- ushort bitsSet[kNumChars],sortedCharP[kNumChars];
- //
- // charVals[c] is the character to be output for a character
- // size piece of the bitmap with c bits set
- //
- ushort charVals[1+kMaxCharVals];
-
- register ulong *q,*rowP;
- register short count,numBitsSet;
- register ulong mask;
-
- register uchar *p;
- ulong origMask;
- ushort lineBytes;
- short rCnt,rowBytes,hPix,fontNum,theErr;
-
- GetFNum(fontName,&fontNum);
- if (0 == fontNum) return (kErr);
- if (!RealFont(fontNum,fontSize)) return (kErr);
- GetPort(&savePort);
- OpenPort(&charPort);
- TextFont(fontNum);
- TextSize(fontSize);
- if (theErr = InitOffscreenBitMap(&charPort))
- return theErr;
- //
- // Draw characters in bitmap. Draws them one at a time
- // so we can align the characters within long words.
- //
- DrawTheChars(&charPort);
- SetPort(savePort);
- //
- // Init charVal array of characters to output.
- //
- InitBitsSetArray(charPort.portBits.baseAddr,bitsSet);
- SortBitsSetArray(bitsSet,sortedCharP);
- InitCharPointerArray(bitsSet,sortedCharP,charVals);
- //
- // Process bitMap.
- //
- p = (uchar *)bitMapPtr->baseAddr;
- rowBytes = bitMapPtr->rowBytes;
- lineBytes = rowBytes*lineHeight;
- rCnt = bitMapPtr->bounds.bottom - bitMapPtr->bounds.top;
- hPix = bitMapPtr->bounds.right - bitMapPtr->bounds.left;
- //
- // Set a mask of charWid characters using a sign-extended
- // shift.
- //
- origMask = mask = (ulong)
- ((signed long)0x80000000>>(charWid-1));
- //
- // Loop on rows of characters.
- //
- do {
- short numBits,bitsThisChunk,bitsToGo,hCnt;
- rowP = (ulong *)p;
- bitsThisChunk = kBitsPerChunk;
- hCnt = hPix;
- numBits = bitsToGo = charWid;
- //
- // Loop on chars within current row.
- //
- do {
- //
- // Count bits set in current char.
- //
- numBitsSet=0;
- //
- // Loop on pixels in this chunk.
- //
- do {
- q = rowP;
- count = lineHeight;
- //
- // Count number of bits set in this chunk.
- //
- do {
- register ulong ch;
- BitCount(ch,*q & mask,numBitsSet);
- q = (ulong *)((uchar *)q + rowBytes);
- } while (--count);
- //
- // Continue processing current char if there are bitsToGo.
- //
- if (0 == (bitsThisChunk -= numBits)) {
- // Check for end of row.
- if (hCnt < (bitsThisChunk=kBitsPerChunk))
- bitsThisChunk = hCnt;
- ++rowP;
- if (bitsToGo-=numBits) {
- if (bitsToGo > hCnt) bitsToGo = hCnt;
- mask = origMask << (charWid - bitsToGo);
- numBits = bitsToGo;
- } else {
- mask = origMask;
- break;
- }
- } else if (numBits != charWid) {
- mask = (origMask >> bitsToGo);
- break;
- } else {
- mask >>= numBits;
- break;
- }
- } while (true); /* break if (0 == bitsToGo) */
- numBits = bitsToGo = charWid;
- if (numBits>bitsThisChunk) numBits=bitsThisChunk;
- //
- // Select output character;
- //
- if (numBitsSet<kMaxCharVals)
- count = *(charVals+numBitsSet);
- else count = *(charVals+kMaxCharVals);
- putc(count,outputFile);
- } while (0 < (hCnt-=charWid));
- p += lineBytes;
- putc(EOL,outputFile);
- if (0 > rCnt) break;
- if (0 > (rCnt-=lineHeight)) lineHeight += rCnt;
- mask = origMask;
- } while (true);
- return 0;
- }
-